package scala.subscala

import scala.util.parsing.combinator._
import scala.util.parsing.combinator.lexical._
import scala.util.parsing.combinator.syntactical._
import scala.Predef.{stringWrapper => _, _} // disable the stringWrapper implicit conversion
import scala.util.~
  
/** Parser for a SUBset of Scala
 */  
trait Parser extends StdTokenParsers with BindingParsers with ImplicitConversions with SyntaxIsMappable {
  implicit def TildeIsMappable[t <% Mappable[t], u <% Mappable[u]](p: t ~ u): Mappable[t ~ u] = new Mappable[t ~ u] {
    def gmap(f: Mapper): t ~ u = p match { case a ~ b => scala.util.~(f(a), f(b)) }
  }  
  
  type Tokens = StdLexical 
  val lexical = new StdLexical
  import lexical.{token, Identifier}
  
  lexical.reserved ++= List("new", "unit", "Unit", "val", "def", "type", "trait", "abstract", "class")
  lexical.delimiters ++= List(".", "(", ")", "{", "}", "=", "<:", ":>", "[", "]", ":", ",")

  //object ClassKind extends Enumeration { val Abstract, Concrete, Trait = Value }
  
  // name = ident
  def name :Parser[Name] = accept("name", {case Identifier(n) => Name(n)})
  
  // label = ident
  def label: Parser[String] = accept("label", {case Identifier(n) => n})
  
  // path = bound(name) opt('.' repsep(name, '.'))
  def path :Parser[Path] = bound(name) ~ (("." ~ name*("."))?) ^^ headOptionTailToFunList(Path)

  def target: Parser[Term] =  
     ( "new"  ~! typeP  ~ opt(actualsV)                                        ^^ flatten2(New) 
     | "unit"                                                                  ^^ TMUnit
     | bound(name)                                                             ^^ Path_.fromName
     | "(" ~ term ~ ")"
     )

   // a term starts with a target, followed by any number of (field selection or method call)'s, separated by "."
   def term: Parser[Term] =   {
      /** Combine a term and a method call or field selection into a new term
       *
       * @pre tgt.isPath implies tgt.isInstanceOf[Path]
       * @post @result.isPath implies @result.isInstanceOf[Path]
       */
      def combine(tgt: Term, sel: ~[Name, Option[List[Term]]]) = sel match {
        case (name@Name(n)) ~ Some(args) => MethodCall(tgt, n, args)
        case (name@Name(n)) ~ None => tgt match {
          case p: Path => p ++ Path_.fromName(name) 
          case _ => FieldSel(tgt, n)
        }
      }
      
      chainl1(target, name ~ opt(actualsV), "." ^^ &combine)
   }

  /** <pre>
   *  type = sig | 'Unit' | path '.' 'type' | path   
   *  </pre>   
   */
  def typeP: Parser[Type] = 
     (  
       "Unit"              ^^ TPUnit
     | path ~ "." ~ "type" ^^ SingletonType
     | path                ^? {case target: Path if target.comps.length > 1 => target.split match {case (qual, name) => TypeSel(qual, name) }}
     // there are no user-defined top-level classes, hence the condition target.comps.length > 1
     )                           
   
  def scoped = newScope[Name]

  def classkw = "trait" // (("abstract" ~ "class") ^^ ClassKind.Abstract | "trait" ^^ ClassKind.Trait | "class" ^^ ClassKind.Concrete)
  
  def sig = classkw ~ name ~ (scoped >> {f => opt(formalsV(f)) ~ (opt("extends") >> {x => in(template(x), f)})})
    
  def template(parentsFollow: Boolean) 
     = ((if(parentsFollow) (typeP ~ opt(actualsV) ~ repsep(typeP, "with")) ^^ {case parent ~ args ~ parents => (args, (parent :: parents))}
         else success((None, Nil)))
       ~ classBody) ^^ {case (args, parents) ~ (selftp ~ mems) => Template(args, parents, selftp, mems)}
  
  
  def classBody: Parser[Type ~ UnderBinder[Name, List[Member]]] 
     = scoped >> { self => "{" ~ (bind(name, self) ~ ":" ~ typeP | bind(success(Name("this")), self) ~ success(Self)) ~ "=>" ~ in(rep(member), self) ~ "}" } 

  /** <pre>
   *  member = 
   *      'val'   label ':' type opt('=' term)
   *    | 'type'  label opt('=' type)
   *    | 'trait' label sig   
   *    | 'def'   label '(' repsep(bind(name, as) ':' type, ',') ')' ':' type opt('=' in(term, as))?
   *  </pre>
   */
  def member: Parser[Member] = 
    ( "val"   ~! label ~ ":" ~ typeP ~ opt("=" ~! term) ^^ flatten3(MField)
    | "type"  ~! label ~ opt("=" ~! typeP)              ^^ flatten2(MType)
    | classkw ~! label ~ sig                            ^^ flatten2(MTrait)
    | "def"   ~! label ~ (scoped >> {s => in(opt(formalsT(s.allowForwardRef)) ~ opt(formalsV(s))  
                       ~ ":" ~ typeP ~ opt("=" ~! term), s)})     ^^ flatten2(MMethod)   
    )

  def actualsV: Parser[List[Term]] = "(" ~ term*(",") ~ ")"
  def actualsT: Parser[List[Type]] = "[" ~ typeP*(",") ~ "]"
    
  def formalsV(scope: Scope[Name]) = "(" ~ ((bind(name, scope) ~ ":" ~! typeP)*(",")) ~ ")"
  def formalsT(scope: Scope[Name]): Parser[List[Param]] 
     = "[" ~ ((bind(name, scope) ~  (nested(scope) >> {ns => in(opt(formalsT(ns)) ~ opt(bounds), ns)})  ^^ Param)*(",")) ~ "]"
     
  def bounds = "<:" ~ typeP
  
}



 
 /* def term = target ~ rep("." ~ name ~ opt(actuals))        ^^ flatten2(mkTerm)
 def mkTerm(tgt: Term, arg_sels: List[{Name, Option[List[Term]]}]): Term = {
    // @pre tgt.isPath implies tgt.isInstanceOf[Path]
    def buildSelection(tgt: Term, arg_sel: {Name, Option[List[Term]]}): Term = arg_sel match {
      case {name@Name(n), None} => tgt match {case p: Path => p ~ Path_.fromName(name) case _ => FieldSel(tgt, n)}
      case {name@Name(n), Some(args)} => MethodCall(tgt, n, args) 
    }
      
    arg_sels.foldLeft(if(tgt.isPath) Path_.fromTerm(tgt) else tgt)(buildSelection)
  }*/
    
  /*def debug(msg: String) = new UnitParser {
   def apply(in: Input) = {Console.println(msg); Success((), in)}
 }*/
   
   